"""
@Time : 2020-12-15 11:54
@Author: langengel
@Des: 微信SDK
"""

import time
from fastapi import Request, HTTPException, Query
from wechatpy import WeChatClient
from wechatpy.client.api import WeChatMessage
from wechatpy.oauth import WeChatOAuth
from application.service.login import wechat_to_user
from application.service.reg import user_bind
from config.config import QRCODE_EXPIRE, REG_EXPIRE, USER_BIND
from saas.response.BaseResponse import fail, success
from saas.utils.utils import req_code
from config.wechat import APP_ID, SECRET, SCOPE, REDIRECT_URI, STATE, SUBSCRIBE_ID
from models.saas import Wechat, User
import json

# Client
WeChatClient = WeChatClient(
    appid=APP_ID,
    secret=SECRET,
)

# 微信公众号授权
OAuth = WeChatOAuth(
    app_id=APP_ID,
    secret=SECRET,
    scope=SCOPE,
    state=STATE,
    redirect_uri=REDIRECT_URI + "/wechat/call"
)

subscribe_authorize_url = WeChatMessage(WeChatClient).get_subscribe_authorize_url(
    scene=1,
    template_id=SUBSCRIBE_ID,
    redirect_url=REDIRECT_URI + "/docs"
)


def authorize_url(state: str) -> str:
    """
    构造微信授权地址
    :param state: 标志
    :return:
    """
    # url编码
    OAuth.state = state

    url = OAuth.authorize_url + '&t=' + str(int(time.time()))
    return url


async def create_qrcode_url(req: Request, scan_type: str = Query(..., description="扫码类型")):
    """
    创建微信授权二维码链接
    :param req: 请求头
    :param scan_type: 二维码类型
    :return: dict
    """
    code = req_code()
    if not code:
        return fail("禁止访问")
    business = [
        # 登录
        'login',
        # 注销
        'destruction',
        # 找回密码
        'forgone_password',
        # 注册
        'register',
        # 订阅
        'subscribe',
    ]

    if scan_type not in business:
        return fail(msg="暂未提供此项服务", data={
            "web_code": None,
            "wechat_qrcode": None
        })

    if scan_type == 'subscribe':
        # qrcode_img = qr_code(subscribe_authorize_url)
        # qrcode_img = icon_qr_code(subscribe_authorize_url)
        # show_qrcode(qrcode_img)
        # img_base64_data = img_base64(qrcode_img)
        # 返回生成的订阅二维码
        return success({
            "web_code": code,
            'qrcode_url': subscribe_authorize_url
        }, msg='微信授权码')

    # 编码成带json字符串
    data = json.dumps({"scan": scan_type, "host": req.client.host})

    return await set_new_scan(req=req, web_code=code, data=data, expire=QRCODE_EXPIRE)


async def call(req: Request, code: str, state: str):
    """
    微信扫码回调
    :param req:
    :param code:
    :param state:
    :return: html
    """
    # 获取请求码
    web_code = state
    # 获取redis中的数据
    data = await req.app.state.cache.get(web_code)
    if not data:
        return fail(msg="二维码已过期")

    # 数据json解码
    scan_data = json.loads(data)
    # 扫码类型
    scan_type = scan_data["scan"]

    # 二维码是否有效
    if not scan_type:
        return fail(msg="二维码已过期")

    # 拉取用户信息
    # 获取到code,
    try:

        # 获取access_token
        user_openid = OAuth.fetch_access_token(code)
        # 是否获取到access_token
        if 'openid' not in user_openid and 'access_token' not in user_openid:
            return fail(msg="系统繁忙稍后重试")
        type_list = [
            # 微信登录
            'login',
            # 微信注册
            'register',
            # 注销
            'destruction',
            # 找回密码
            'forgone_password',
            # 管理员绑定
            'user_bind'
        ]
        if scan_type not in type_list:
            return fail(msg="暂未开放此项服务")
        user = {}
        if scan_type == "login":
            # 查询用户是否绑定
            wechat_user = await Wechat().filter(openid=user_openid['openid'])

            if not wechat_user:
                # 设置扫码授权完成
                action = json.dumps({"scan": "no-bind"})
                await req.app.state.cache.set(key=web_code, value=action, expire=QRCODE_EXPIRE)
                return fail(msg="当前微信未绑定服务商账号")

            user = wechat_user[0].__dict__
            user.pop("create_time")
            user.pop("update_time")
            # # 去除多余字段
            # user = {
            #     'city': user['city'],
            #     'country': user['country'],
            #     'headimgurl': str(user['headimgurl']).replace("https://", "//"),
            #     'nickname': user['nickname'],
            #     'openid': user['openid'],
            #     'province': user['province'],
            #     'sex': user['sex']
            # }
        # 服务商注册
        if scan_type == 'register' or scan_type == 'user_bind':
            # 拉取用户信息
            user = OAuth.get_user_info(user_openid['openid'], user_openid['access_token'])
            # 去除多余字段
            user = {
                'city': user['city'],
                'country': user['country'],
                'headimgurl': str(user['headimgurl']).replace("https://", "//"),
                'nickname': user['nickname'],
                'openid': user['openid'],
                'province': user['province'],
                'sex': user['sex']
            }

        # 判断openid是否存在
        if 'openid' not in user:
            # 授权失败
            action = json.dumps({"scan": "privilege_grant_failed"})
            await req.app.state.cache.set(key=web_code, value=action, expire=QRCODE_EXPIRE)
            return fail(msg="获取微信授权失败，请重新扫码授权")

        # 将拉用户信息缓存到Cache
        await req.app.state.cache.set(key=web_code + '-userinfo', value=json.dumps(user), expire=REG_EXPIRE)

        # 返回授权页面数据
        user_data = {
            "headimgurl": user['headimgurl'],
            "nickname": user['nickname'],
            "scan_type": scan_type,
            "host": scan_data["host"]
        }
        return success(data=user_data, msg="获取授权成功")

    except HTTPException as e:
        print(e)
        return fail(msg="微信授权接口异常")


async def wechat_confirm(req: Request, state: str, scan_type: str):
    """
    扫码确认操作
    :param req:
    :param state:
    :param scan_type:
    :return:
    """
    if scan_type == 'user_bind':
        return await user_bind(req=req, web_code=state)
    # 设置扫码授权完成
    action = json.dumps({"scan": scan_type + '-finish'})
    await req.app.state.cache.set(key=state, value=action, expire=QRCODE_EXPIRE)

    return success(msg="已成功登陆")


async def check_scan(req: Request, web_code: str = Query(..., min_length=32, max_length=32)):
    """
    验证扫码是否成功
    :param req:
    :param web_code: 请求码
    :return:
    """

    # 获取缓存中的微信用户
    scan = await req.app.state.cache.get(web_code)

    if not scan:
        return fail(code=2001, msg="二维码已过期")

    # 扫码类型数据解码
    scan_type = json.loads(scan)

    # 授权失败
    if scan_type["scan"] == "privilege_grant_failed":
        # 删除浏览器标识缓存
        await req.app.state.cache.delete(key=web_code)
        return fail(code=4000, msg="获取微信授权失败，请重新扫码授权")

    # 未绑定
    if scan_type["scan"] == "no-bind":
        # 删除浏览器标识缓存
        await req.app.state.cache.delete(key=web_code)
        return fail(code=4001, msg="当前微信未绑定服务商账号")

    # 获取缓存中的微信用户信息
    wx_user = await req.app.state.cache.get(web_code + '-userinfo')

    if not wx_user:
        return fail(msg="等待微信扫码授权")

    # 提取用户信息
    user = json.loads(wx_user)

    if scan_type["scan"] == 'register-finish':
        # 查询用户是否绑定其他服务商
        # user = await Wechat.get_or_none(openid=user['openid'])
        # if user:
        #     return fail(code=201, msg="此微信号已经绑定其他账户")
        # 删除客户端凭证
        await req.app.state.cache.delete(web_code)
        return success(msg="注册扫码成功")

    if scan_type["scan"] == 'login-finish':
        # 微信登录
        # 通过openid查询用户拥有的服务商
        service_list = await wechat_to_user(openid=user['openid'])
        if not service_list:
            return fail(msg="当前微信未绑定服务商账号", code=201)

        # 缓存当前微信用户拥有的服务商
        await req.app.state.cache.set(web_code + '-myService', json.dumps(service_list), expire=REG_EXPIRE)
        # 删除微信用户信息缓存
        await req.app.state.cache.delete(key=web_code + '-userinfo')

        return success(data=[], msg="登陆扫码成功")

    return fail(msg="等待微信扫描")


async def user_wechat_bind(req: Request, uid: int):
    web_code = req_code()
    if not web_code:
        return fail("禁止访问")

    if not req.state.sid or not uid:
        return fail(msg="服务商内部接口请登陆调用")
    # 非超级管理员不能换绑超级管理员微信
    user = await User().get_or_none(id=uid)
    if not req.state.is_admin and user.type:
        return fail(msg="非超级管理员不能换绑超级管理员微信")
    # 编码成带json字符串
    data = json.dumps({"scan": "user_bind", "host": req.client.host, "sid": req.state.sid, "uid": uid})

    return await set_new_scan(req=req, web_code=web_code, data=data, expire=USER_BIND)


async def set_new_scan(req: Request, web_code: str, data: str, expire: int):
    """
    缓存扫码数据
    :param req:
    :param web_code: 请求码
    :param data: 要缓存的数据
    :param expire: 过期时间
    :return:
    """
    # 删除之前的标识身份
    await req.app.state.cache.delete(key=web_code)

    await req.app.state.cache.delete(web_code + '-userinfo')

    # 设置新的标识状态为等待扫码
    await req.app.state.cache.set(key=web_code, value=data, expire=expire)

    # 返回生成的二维码的链接
    return success({
        "web_code": web_code,
        'qrcode_url': authorize_url(state=web_code),
        'expires_time': expire
    }, msg='微信授权码')

# data = {
#     "first": {
#         "value": "黄先生",
#         "color": "#000000"
#     },
#     "keyword1": {
#         "value": "黄先生",
#         "color": "#000000"
#     },
#     "keyword2": {
#         "value": "黄先生",
#         "color": "#000000"
#     },
#     "keyword3": {
#         "value": "黄先生",
#         "color": "#000000"
#     },
#     "remark": {
#         "value": "黄先生",
#         "color": "#000000"
#     },
# }

# return success(data={'ba': img_base64(str_qrcode(subscribe_authorize_url))})
# WeChatMessage(WeChatClient).send_template(
#     user_id="o1Dd2wHyodDZagy3FZXDImDxYFn4",
#     template_id="xho477s_uSMMYfxWfVmvpnocTs8-MC0Ux1clE1IO_8E",
#     data=data
# )
# WeChatMessage(WeChatClient).send_subscribe_template(
#     openid="o1Dd2wHyodDZagy3FZXDImDxYFn4",
#     template_id="xho477s_uSMMYfxWfVmvpnocTs8-MC0Ux1clE1IO_8E",
#     scene=1,
#     title="你好，你的帐号已开通成功",
#     data=data,
#     url=None
#
